package com.samskievert.cactusboom;

import java.util.ArrayList;
import java.util.Arrays;
import com.threerings.util.Name;
import java.awt.Point;

/**
 * Performs some analysis of Reversi board state to determine move legality and
 * piece flipping.
 */
public class CBLogic
{
    public CBLogic (int size)
    {
        _size = size;
        _state = new int[size*size];
        _stateTerr = new int[size*size];
        _claims = new int[size*size];
        //slightly hackey to set it to 4 instead of NUMPLAYERS, but it'll work for now
        _turnpoints = new int[4];
        _movepoints = new int[4];
    }

    public void setState (CBObject game)
    {
        Arrays.fill(_state, -1);
        Arrays.fill(_stateTerr, -1);
        Arrays.fill(_claims,-1);
        
        for (CBObject.Cactus cactus : game.pieces) {
            _state[cactus.y * _size + cactus.x] = cactus.owner;
            
        }
        
        //it actually matters that pickups come before cowboys, as there
        //is a brief instant when they overlap...causing the game to think
        //there are no legal moves
        for (CBObject.Pickup pickup : game.pickups) {
            _stateTerr[pickup.y * _size + pickup.x] = -2;
        }
                
        for (CBObject.Cowboy cowboy : game.cowboys) {
            _stateTerr[cowboy.y * _size + cowboy.x] = cowboy.owner;
            if(game.movePoints[cowboy.owner] > 0) _activeCowboy = cowboy;
       }
       
       for (CBObject.Claim claim : game.claims) {
            _claims[claim.y * _size + claim.x] = claim.owner;
       }

        _turnpoints = game.turnPoints;
        _movepoints = game.movePoints;
    }
    
    /** 
     * Returns how many pieces are surrounding the tile the current cowboy is standing on.
     * This is used for difficulty selection on the puzzles.
     */
    public int getDifficulty(CBObject game, int whichPlayer)
    {
        //for claim at index i, where I = claim.y*_size + claim.x
        // (0,1)  = i + _size
        // (-1,0) = i-1
        // (0,-1) = i - _size
        // (1,0)  = i + 1
        int difficulty = 0;
        CBObject.Cowboy cowboy = game.cowboys.get(whichPlayer);
        if(cowboy != null){
            
        int currentLoc = cowboy.y * _size + cowboy.x;
        int CC = _claims[currentLoc]; //Current Color

        //if it's on a border, give it a favorable terrain bonus
        if(cowboy.y + 1 >= _size || _claims[currentLoc + _size] == CC) difficulty++;
        if(cowboy.x - 1 <  0     || _claims[currentLoc -     1] == CC) difficulty++;
        if(cowboy.y - 1 <  0     || _claims[currentLoc - _size] == CC) difficulty++;
        if(cowboy.x + 1 >= _size || _claims[currentLoc +     1] == CC) difficulty++;
        //if it's unowned...make it easy
        if(_claims[currentLoc] == -1) difficulty = 0;
        
        //System.out.println("Difficulty: " + difficulty);
        }
        //return 5;
        return difficulty + 1;
    }
    
    
    /**
     * Returns the index into the {@link CBObject#players} array of the
     * player to whom control should transition.
     */
    public int getNextTurnHolderIndex (int curTurnIdx, CBObject gameobj)
    {
        // there's probably a more elegant way to do this somewhere in the library...
        Name[] names = gameobj.getPlayers();
        int nextTurn = curTurnIdx + 1;
        if(nextTurn > names.length-1){  //length will be NUMPLAYERS, but the turn value starts at 0
            nextTurn = 0;
            //assuming we started with player 0, this also means that a full
            //round has gone by...which means it's time to add more spikes
            //to all the current cacti!
        }
        return (int)nextTurn;
        //if I wanted to end after a certan # of rounds or points, I could
        //run a check here and return -1 for game over
        //return -1;
    }

    /**
     * Returns true if the supplied piece represents a legal move for the
     * owning player.
     */
    public boolean isLegalMove (CBObject.Cactus cactus, int whichLayer)
    {
        boolean legalMove = false;
        if(cactus != null && _movepoints != null){
           if(whichLayer == 1){
                if (inBounds(cactus.x, cactus.y) && getColor(cactus.x,cactus.y) == cactus.owner) legalMove = true;
                if (inBounds(cactus.x, cactus.y) && getColor(cactus.x, cactus.y)== -1) legalMove = true;
            }else{
               
               
               if (getMoveDistance(_activeCowboy,cactus) <= _movepoints[cactus.owner] && inBounds(cactus.x,cactus.y) && getTerrainColor(cactus.x, cactus.y) < 0){
                    
                    if(isLegalDestination(cactus) > 0) legalMove = true;
               }else{
                //System.out.println("getMoveDistance(_activeCowboy,cactus) returns " + getMoveDistance(_activeCowboy,cactus) + " <= " + _movepoints[cactus.owner]);
                //System.out.println("getTerranColor(" + cactus.x + " " + cactus.y + ") returns " + getTerrainColor(cactus.x, cactus.y));
                //if(!inBounds(cactus.x,cactus.y)) System.out.println("...and we're out of bounds!");
               }
                //System.out.println("Cactus owner is " + cactus.owner);
            }
        }
        return legalMove;
    }
    
    public int getMoveDistance(CBObject.Cowboy myPos, CBObject.Cactus cursorPos)
    {
        return(Math.abs(cursorPos.x - myPos.x) + Math.abs(cursorPos.y-myPos.y));       
    }
    
    public int getMoveType (CBObject.Cactus cactus, int whichLayer)
    {
        if(isLegalMove(cactus,whichLayer)){
            if(whichLayer == 1){ //use _state
            //if we're over an empty space, it's a new move
            if(getColor(cactus.x,cactus.y) == -1) return 1;
            //otherwise we're adding on to an existing move
            if(getColor(cactus.x,cactus.y) == cactus.owner) return 2;            
            }else{      //use _stateTerr
            if(getTerrainColor(cactus.x,cactus.y) <= -1) return 3;
            }
        }
        
        //no legal moves, return 0
        return 0;
    }
    
    
    public int getDirection(Point b){
        Point a = new Point(_activeCowboy.x,_activeCowboy.y);
        //Point b = new Point(c.x,c.y);
        if(a.x == b.x && a.y < b.y) return 0;
        if(a.x > b.x && a.y == b.y) return 1;
        if(a.x == b.x && a.y > b.y) return 2;
        if(a.x < b.x && a.y == b.y) return 3;        
        return -1;
    }
    
    //find out which cactus i'm hovering over (and return null if I'm not)
    public CBObject.Cactus getOverlap (Iterable<CBObject.Cactus> pieces, int x, int y)
    {
        CBObject.Cactus c = null;
        for (CBObject.Cactus cactus : pieces) {
            if(cactus.x == x && cactus.y == y) return cactus;
        }
        return c;
    }
    
    
    /**
     * Returns true if the player with the specified color has legal moves.
     * I'm using this slightly differently than Reversi... because the player
     * can make more than one move per turn, after placing a piece I check to see
     * if there are any more legal moves.  If yes, the player can keep going.  If
     * not, pass the turn onto the next player.
     */
    public boolean hasLegalMoves (int color)
    {
        //if i have any moves left
        if(_movepoints[color] > 0) return true;
                
        //otherwise search every board position for a legal move
        CBObject.Cactus cactus = new CBObject.Cactus();
        cactus.owner = color;
        for (int yy = 0; yy < _size; yy++) {
            for (int xx = 0; xx < _size; xx++) {
                cactus.x = xx;
                cactus.y = yy;
                 //if i have any cacti to click on...
                 if(getMoveType(cactus,1) == 2) return true;
           }
        }
        
        //if I make it this far, I must not have any legal moves...
        //DEBUG
        
        //System.out.println("Tested for Player " + color);
        
        return false;
    }

    
    //this function returns the minimum number of moves needed
    //to get from currentLoc to goalLoc
    public int isLegalDestination(CBObject.Cactus cactus)
    {
     int rVal = -1;
     if(_movepoints != null)
     {
     
     
     int numMoves = _movepoints[cactus.owner];
     Point currentLoc = new Point(_activeCowboy.x,_activeCowboy.y);
     Point goalLoc = new Point(cactus.x, cactus.y);
             
     ArrayList<Point> r = new ArrayList<Point>();
     ArrayList<movePath> paths = new ArrayList<movePath>();
     ghostMove(currentLoc,numMoves,r,paths,new movePath(currentLoc));
     
     movePath quickPath = null;
     for(int i = 0; i < paths.size(); i++)
        {
            movePath path = (movePath)paths.get(i);
            if(path.containsPoint(goalLoc) != -1){
                if(quickPath == null) quickPath = new movePath(path,0,path.containsPoint(goalLoc));
                if(path.containsPoint(goalLoc) > 0 && path.containsPoint(goalLoc) < quickPath.pathPoints.size()) quickPath = new movePath(path,0,path.containsPoint(goalLoc));
            }
        }
     
     if(quickPath != null) rVal = quickPath.pathPoints.size();
     
     }
     return rVal;
    }
    
    //this is basically the same function, only it returns the actual path
    public movePath getPath(CBObject.Cactus cactus)
    {
     movePath rPath = null;
     if(_movepoints != null)
     {
     
     
     int numMoves = _movepoints[cactus.owner];
     Point currentLoc = new Point(_activeCowboy.x,_activeCowboy.y);
     Point goalLoc = new Point(cactus.x, cactus.y);
             
     ArrayList<Point> r = new ArrayList<Point>();
     ArrayList<movePath> paths = new ArrayList<movePath>();
     ghostMove(currentLoc,numMoves,r,paths,new movePath(currentLoc));
     
     movePath quickPath = null;
     for(int i = 0; i < paths.size(); i++)
        {
            movePath path = (movePath)paths.get(i);
            if(path.containsPoint(goalLoc) != -1){
                if(quickPath == null) quickPath = new movePath(path,0,path.containsPoint(goalLoc));
                if(path.containsPoint(goalLoc) > 0 && path.containsPoint(goalLoc) < quickPath.pathPoints.size()) quickPath = new movePath(path,0,path.containsPoint(goalLoc));
            }
        }
     
     if(quickPath != null) return new movePath(quickPath,1,quickPath.pathPoints.size()); //we don't really need our current location...
     
     }
     return rPath;
    }
    
    
    
    public void ghostMove(Point currentLoc, int numMoves, ArrayList<Point> moveList, ArrayList<movePath> paths, movePath pathSoFar)
    {
        int[] MX = { 0,-1, 0, 1};
        int[] MY = { 1, 0,-1, 0};
        if(numMoves == 0){  //if i am a final move, add me to the moveList (assuming not already there)
             if(!moveList.contains(currentLoc)) moveList.add(currentLoc);
             paths.add(pathSoFar);
        }else{  //pass the move on to every valid direction
            for(int i = 0; i < 4; i++){
            int tx = currentLoc.x + MX[i];
            int ty = currentLoc.y + MY[i];
            if(inBounds(tx,ty) && getTerrainColor(tx,ty) < 0){
            ghostMove(new Point(tx,ty), numMoves - 1, moveList, paths, new movePath(pathSoFar, new Point(tx,ty)));
       }
     }   
  }
        
    }
    
    /**
     *This method tests to see if a given square is surrounded on all sides.
     */
    
    protected boolean isSurrounded(int x, int y)
    {
        int[] moveX = {  0, -1,  0, 1 };
        int[] moveY = {  1,  0, -1, 0 }; 
        
        for(int i = 0; i < 4; i++){
            int tx = x + moveX[i];
            int ty = y + moveY[i];
            if(inBounds(tx,ty) && getColor(tx,ty) < 0) return false;
        }
        
        return true;
    }

    protected final int getColor (int x, int y)
    {
        if(inBounds(x,y))return _state[y * _size + x];
        return -1;
    }
    
    protected final int getTerrainColor (int x, int y)
    {
        return _stateTerr[y * _size + x];
    }

    protected final boolean inBounds (int x, int y)
    {
        return x >= 0 && y >= 0 && x < _size && y < _size;
    }

    protected int _size;
    protected int[] _state;
    protected int[] _stateTerr;
    protected int[] _turnpoints;
    protected int[] _movepoints;
    protected int[] _claims;
    protected CBObject.Cowboy _activeCowboy;

    protected static final int[] DX = { -1, 0, 1, -1, 1, -1, 0, 1 };
    protected static final int[] DY = { -1, -1, -1, 0, 0, 1, 1, 1 };
    
    public static class movePath
    {
        ArrayList<Point> pathPoints;
 
        movePath(Point p){  //initialize it with the starting point
            pathPoints = new ArrayList<Point>(); 
            pathPoints.add(p);
        }
 
        movePath(movePath p, Point np){  //initialize it with the steps up to that point and the new point
            pathPoints = new ArrayList<Point>();
            for(int i = 0; i < p.pathPoints.size(); i++){
                Point pt = (Point)p.pathPoints.get(i); 
                pathPoints.add(pt);
            }
            pathPoints.add(np);
        }
 
        movePath(movePath p, int low, int high){  //intializes a subPath
            pathPoints = new ArrayList<Point>();
            for(int i = low; i < high; i++){
                Point pt = (Point)p.pathPoints.get(i);
                pathPoints.add(pt);
            }
        }
 
        public String toString(){
            String r = "";
            for(int i = 0; i < pathPoints.size(); i++){
                Point p = (Point)pathPoints.get(i);
                r += "(" + p.x + "," + p.y + ") ";   
            }
            return r;
        }
 
        int containsPoint(Point pt){
            for(int i = 0; i < pathPoints.size(); i++){
                Point p = (Point)pathPoints.get(i);
                if(p.equals(pt)) return i+1;  //the (i+1)th element
            }
            return -1;   
        }
 
    }
    
}




